package io.gitee.pkmer.mybatis.support;


import io.gitee.pkmer.common.util.StrUtil;
import io.gitee.pkmer.convention.page.PageResponse;
import io.gitee.pkmer.convention.page.query.FilterCondition;
import io.gitee.pkmer.convention.page.query.PageQuery;
import io.gitee.pkmer.util.ReflectUtil;
import jakarta.annotation.PostConstruct;
import lombok.Getter;
import lombok.Setter;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.apache.ibatis.type.TypeReference;
import org.mybatis.dynamic.sql.AliasableSqlTable;
import org.mybatis.dynamic.sql.SqlColumn;
import org.mybatis.dynamic.sql.render.RenderingStrategies;
import org.mybatis.dynamic.sql.select.render.SelectStatementProvider;
import org.mybatis.dynamic.sql.util.mybatis3.CommonSelectMapper;
import org.mybatis.dynamic.sql.where.WhereApplier;
import org.mybatis.dynamic.sql.where.WhereDSL;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;

import static org.mybatis.dynamic.sql.SqlBuilder.*;

/**
 * @author <a href="mailto:1193094618@qq.com">pkmer</a>
 * <br>
 * <a href = "https://gitee.com/developeros/videos-online">Code Repository</a>
 * At 2024/9/4
 * Mybatis Dynamic Sql Page Query Support
 */
abstract public class DynamicPageQuerySupport<T, TABLE extends AliasableSqlTable<TABLE>> extends GenericTypeProcess<T> {
    public static final Logger logger = LoggerFactory.getLogger(DynamicPageQuerySupport.class);
    /**
     * table 对应的所有column
     * key: column name
     * value: SqlColumn
     */
    public Map<String, SqlColumn<?>> columnMap = new HashMap<>();

    /**
     * 扩展类型
     * 保存所有既是 Enum 又实现了 BaseEnum 接口的类型
     */
    @Getter
    private Set<Class<? extends Enum<?>>> extTypes = new HashSet<>();

    /**
     * 类型处理器
     */
    public Map<String, TypeHandler<?>> typeHandlers = new HashMap<>();

    /**
     * 真实的表
     */
    private TABLE table;

    @Setter(onMethod_ = @Autowired)
    private SqlSessionFactory sqlSessionFactory;

    public DynamicPageQuerySupport() {
        initTable();
    }

    /**
     * 初始化表
     */
    @SuppressWarnings("unchecked")
    public void initTable(){
        Class<T> tableType = getType();
        // 获取具体的table
        Field[] declaredFields = tableType.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            Class<?> type = declaredField.getType();
            if (AliasableSqlTable.class.isAssignableFrom(type)) {
                this.table = (TABLE) ReflectUtil.getStaticFieldValue(declaredField);
            }
        }
    }

    @PostConstruct
    public void init(){
        CompletableFuture.runAsync(this::initColumnMap);
    }

    /**
     * 初始化列
     */
    public void initColumnMap() {
        Field[] fields = getType().getDeclaredFields();
        for (Field field : fields) {
            // 确保字段是 SqlColumn 类型
            if (SqlColumn.class.isAssignableFrom(field.getType())) {
                // 获取字段值，即 SqlColumn 对象
                SqlColumn<?> sqlColumn = (SqlColumn<?>) ReflectUtil.getStaticFieldValue(field);
                // 将字段名和 SqlColumn 对象添加到 Map 中
                columnMap.put(sqlColumn.name(), sqlColumn);
                doRegister(sqlColumn,field);
            }
        }
    }

    @SuppressWarnings("unchecked")
    public void doRegister(SqlColumn<?> sqlColumn,Field sqlColumnField){
        Class<?> javaType = null;
        if(sqlColumn.javaType().isPresent()){
            javaType = sqlColumn.javaType().get();
        }else{
            Type genericType = sqlColumnField.getGenericType();
            if (genericType instanceof ParameterizedType) {
                // 获取实际的泛型类型参数
                Type[] actualTypeArguments = ((ParameterizedType) genericType).getActualTypeArguments();
                if (actualTypeArguments.length > 0) {
                    javaType = (Class<?>)actualTypeArguments[0];
                }
            }
        }

        if (javaType != null && Enum.class.isAssignableFrom(javaType)) {
            registerExtType((Class<? extends Enum<?>>) javaType);
            registerTypeHandler(sqlColumn.name(),javaType);
        }
    }

    /**
     * 注册类型处理器
     * @param javaType java类型
     */
    public void registerTypeHandler(String colunmName,Class<?> javaType){
        TypeHandlerRegistry typeHandlerRegistry = sqlSessionFactory.getConfiguration().getTypeHandlerRegistry();
        TypeHandler<?> typeHandler = typeHandlerRegistry.getTypeHandler(javaType);
        if (typeHandler instanceof TypeReference) {
            Type rawType = ((TypeReference<?>) typeHandler).getRawType();
            if (rawType != null && rawType == javaType) {
                doRegisterTypeHandler(colunmName,typeHandler);
            }
        }
    }


    /**
     * 注册扩展类型
     * @param type 类型
     */
    public void registerExtType(Class<? extends Enum<?>> type){
        this.extTypes.add(type);
    }


    /**
     * 注册对应的类型处理器
     * @param columnName 字段名称
     * @param handler 类型处理器
     */
    public void doRegisterTypeHandler(String columnName,TypeHandler<?> handler) {
        String name = handleBackTrick(columnName);
        this.typeHandlers.put(name,handler);
    }




    /**
     * 分页查询
     *
     * @param pageQuery 分页
     * @param selectMapper mapper
     * @param recordType 返回的类型
     */
    public <R> PageResponse<R> pageQuery(PageQuery pageQuery, CommonSelectMapper selectMapper,Class<R> recordType) {
        WhereApplierSupport whereApplierSupport = new WhereApplierSupport();
        WhereApplier whereApplier = whereApplierSupport.generateWhere(pageQuery.getFilters());
        SelectStatementProvider countStatementProvider = countFrom(table)
                .applyWhere(whereApplier)
                .build()
                .render(RenderingStrategies.MYBATIS3);


        logger.info(countStatementProvider.getSelectStatement());
        Long total = selectMapper.selectOneLong(countStatementProvider);

        SelectStatementProvider selectStatementProvider = select(table.allColumns())
                .from(table)
                .applyWhere(whereApplier)
                .limit(pageQuery.getPageSize())
                .offset((long) pageQuery.getPageSize() * (pageQuery.getPageNo() - 1))
                .build()
                .render(RenderingStrategies.MYBATIS3);

        logger.info(selectStatementProvider.getSelectStatement());

        List<R> rs = selectMapper.selectMany(
                selectStatementProvider,
                this.getMapRecordFunction(recordType));

        return new PageResponse<R>()
                .setTotalPages(total.intValue())
                .setTotalPages(total.intValue() / pageQuery.getPageSize())
                .setList(rs);
    }

    /**
     * 处理结果映射
     * @return Function
     * @param <R> 返回结果类型
     */
    public  <R> Function<Map<String, Object>, R> getMapRecordFunction(Class<R> recordType) {
        return map -> {
            R record = ReflectUtil.newInstance(recordType);

            map.forEach((key, value) -> {
                // 处理reserve world 保留字 并装换成驼峰命名
                String keyStr = handleBackTrick(key);
                TypeHandler<?> typeHandler = typeHandlers.get(keyStr);
                String fieldName = StrUtil.toCamelCase(keyStr);
                if(typeHandler == null){
                    ReflectUtil.setFieldValue(record, fieldName, value);
                }else{
                    // 处理类型处理器这里是对枚举类型的特殊处理
                    try {
                        ResultSetSupport resultSetSupport = new ResultSetSupport(((Integer)value).byteValue());
                        Object result = typeHandler.getResult(resultSetSupport, 0);
                        ReflectUtil.setFieldValue(record, fieldName, result);
                    } catch (Exception e) {
                        throw new RuntimeException("处理枚举转换失败",e);
                    }
                }
            });
            return record;
        };
    }

    /**
     * where条件支持
     */
    class WhereApplierSupport {
        public WhereApplier generateWhere(List<FilterCondition> filters) {


            if (filters == null || filters.isEmpty()) {
                // 返回一个空的 WhereApplier，以避免没有条件时产生错误
                return where(constant("1"),isEqualTo(1)).toWhereApplier();
            }

            WhereDSL.StandaloneWhereFinisher where = where();
            filters.forEach(filter -> handleFilter(where, filter));
            return where.toWhereApplier();
        }

        /**
         * 处理过滤条件转变成SqlColumn强类型
         *
         * @param where  where DSL
         * @param filter 过滤条件
         */
        private void handleFilter(final WhereDSL.StandaloneWhereFinisher where, final FilterCondition filter) {
            SqlColumn<?> sqlColumn = columnMap.get(filter.getField());
            Object value = filter.getValue();

            if (value instanceof Long v) {
                addCondition(where, sqlColumn, v);
            } else if (value instanceof Integer i) {
                addCondition(where, sqlColumn, i);
            } else if (value instanceof String s) {
                addCondition(where, sqlColumn, s);
            } else if (value instanceof Boolean b) {
                addCondition(where, sqlColumn, b);
            } else if (value instanceof Byte b) {
                addCondition(where, sqlColumn, b);
            } else {
                // 处理扩展枚举类型
                for (Class<? extends Enum<?>> extType : extTypes) {
                    if (extType == value.getClass()) {
                        addCondition(where, sqlColumn, value);
                        return;
                    }
                }
                throw new UnsupportedOperationException("不支持的类型");
            }
        }


        /**
         * 添加SqlColumn条件,做类型强转
         *
         * @param where where DSL
         * @param field 对应的SqlColumn
         * @param value 具体的值
         */
        @SuppressWarnings("unchecked")
        private <W> void addCondition(WhereDSL.StandaloneWhereFinisher where, SqlColumn<?> field, W value) {
            // 类型强制转换，确保泛型一致性
            where.and((SqlColumn<W>) field, isEqualTo(value));
        }
    }

    /**
     * 处理保留字 去掉反引号
     * @param column 字段名称
     * @return 处理后的字段名称
     */
    public static String handleBackTrick(String column) {
        return column.replaceAll("`", "");
    }


}
